package de.slothsoft.commons.swing;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import de.slothsoft.commons.res.CommonMessages;

public class LoginPanel extends AbstractDataBindingPanel<LoginPanel.User> {

    private static final long serialVersionUID = 1L;

    private static final String CARD_LOGIN = "login";
    private static final String CARD_AFTER = "after";

    private JComponent loginComponent;
    private JComponent afterLoginComponent;
    private LoginProvider loginProvider;

    private CardLayout layout;

    protected JTextField txtPassword;

    public LoginPanel(JComponent afterLoginComponent, LoginProvider loginProvider) {
	this(createDefaultUser(), afterLoginComponent, loginProvider);
    }

    public LoginPanel(User user, JComponent afterLoginComponent, LoginProvider loginProvider) {
	super(user);

	this.afterLoginComponent = afterLoginComponent;
	this.loginProvider = loginProvider;
	addLayoutComponent(this, this.afterLoginComponent, CARD_AFTER);
	this.txtPassword.requestFocus();
    }

    @Override
    protected void createControls(JPanel mainPanel, DataBinding<User> dataBinding) {
	this.loginComponent = createLoginComponent();
	this.layout = new CardLayout();
	setLayout(this.layout);
	addLayoutComponent(this, this.loginComponent, CARD_LOGIN);
	showCard(CARD_LOGIN);
    }

    private void addLayoutComponent(JPanel parent, JComponent component, String id) {
	this.layout.addLayoutComponent(component, id);
	parent.add(component);
    }

    private void showCard(String id) {
	this.layout.show(this, id);
	this.layout.layoutContainer(this);
    }

    private JComponent createLoginComponent() {
	JPanel basePanel = new JPanel();
	basePanel.setLayout(new CenterLayout());
	basePanel.add(createLoginGroup());
	return basePanel;
    }

    protected JComponent createLoginGroup() {
	JPanel group = new JPanel();
	group.setBorder(BorderFactory.createTitledBorder(CommonMessages.getString("Login.loginGroup")));
	group.setLayout(new GridBagLayout());
	group.setPreferredSize(new Dimension(300, 150));
	int x = 0;
	int y = 0;
	createLabel(group, CommonMessages.getString("Login.login"), x, y++);
	createLabel(group, CommonMessages.getString("Login.password"), x, y++);

	x++;
	y = 0;
	createText(group, "Login", x, y++);
	this.txtPassword = createPassword(group, "Password", x, y++);
	this.txtPassword.addKeyListener(new KeyAdapter() {
	    @Override
	    public void keyTyped(KeyEvent e) {
		if (e.getKeyChar() == 10) {
		    performLogin();
		}
	    }
	});
	group.add(createButtonBar(), GridBagUtil.textFieldDefaults(x, y + 1));

	return group;
    }

    protected Component createButtonBar() {
	JPanel parent = new JPanel();
	parent.setLayout(new BorderLayout());

	int buttonSpacing = 5;

	JPanel buttonPanel = new JPanel();
	buttonPanel.setLayout(new GridLayout(1, 0, buttonSpacing, buttonSpacing));
	buttonPanel.setBorder(BorderFactory.createEmptyBorder(buttonSpacing, buttonSpacing, buttonSpacing,
		buttonSpacing));
	createButtons(buttonPanel);

	parent.add(new JLabel(), BorderLayout.CENTER);
	parent.add(buttonPanel, BorderLayout.EAST);

	return parent;
    }

    protected void createButtons(JPanel buttonPanel) {
	JButton button;

	button = new JButton(CommonMessages.getString("Dialog.login")); //$NON-NLS-1$
	button.addActionListener(new ActionListener() {

	    @Override
	    public void actionPerformed(ActionEvent e) {
		performLogin();
	    }
	});
	button.setMinimumSize(new Dimension(60, 20));
	buttonPanel.add(button);
    }

    protected void performLogin() {
	getDataBinding().updateModels();
	User user = getModel();
	if (this.loginProvider.login(user.getLogin(), user.getPassword())) {
	    performSuccessfulLogin();
	} else {
	    performUnsuccessfulLogin();
	}
    }

    protected void performSuccessfulLogin() {
	showCard(CARD_AFTER);
    }

    protected void performUnsuccessfulLogin() {
	getDataBinding().updateModels();
	JOptionPane.showMessageDialog(this,
		CommonMessages.getString("Login.wrongLoginData"), CommonMessages.getString("ErrorUtil.errorTitle"), //$NON-NLS-1$
		JOptionPane.ERROR_MESSAGE);
	resetPasswordField();
    }

    public void performLogout() {
	showCard(CARD_LOGIN);
	resetPasswordField();
    }

    private void resetPasswordField() {
	getModel().setPassword(null);
	getDataBinding().updateTargets();
	this.txtPassword.requestFocus();
    }

    public JComponent getAfterLoginComponent() {
	return this.afterLoginComponent;
    }

    public LoginProvider getLoginProvider() {
	return this.loginProvider;
    }

    protected static JLabel createLabel(JComponent component, String text, int x, int y) {
	JLabel label = new JLabel(text == null ? "" : text + ":"); //$NON-NLS-1$
	component.add(label, GridBagUtil.labelDefaults(x, y));
	return label;
    }

    /*
	 * 
	 */

    public static User createDefaultUser() {
	User user = new User();
	user.login = CommonMessages.getString("Login.defaultLogin");
	return user;
    }

    public static class User {

	private String login;
	private char[] password;

	public String getLogin() {
	    return this.login;
	}

	public void setLogin(String login) {
	    this.login = login;
	}

	public char[] getPassword() {
	    return this.password;
	}

	public void setPassword(char[] password) {
	    this.password = password;
	}

	@Override
	public String toString() {
	    return this.login + " = " + (this.password == null ? "" : new String(this.password));
	}

    }

    /*
	 * 
	 */

    public static interface LoginProvider {

	boolean login(String login, char[] password);
    }

}
